﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//
using System.IO;
using System.Runtime.InteropServices;



namespace Flac2Wav
{
    class Program
    {
        //
        public enum FLAC__StreamDecoderWriteStatus
        {
            FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE,
            FLAC__STREAM_DECODER_WRITE_STATUS_ABORT
        };
        //
        //#define FLAC__MAX_CHANNELS (8u)
        //#define FLAC__MAX_FIXED_ORDER (4u)
        //#define FLAC__MAX_LPC_ORDER (32u) 

        const uint FLAC__MAX_CHANNELS = 8;
        const uint FLAC__MAX_FIXED_ORDER = 4;
        const uint FLAC__MAX_LPC_ORDER = 32;

        public enum FLAC__StreamDecoderInitStatus
        {
	        FLAC__STREAM_DECODER_INIT_STATUS_OK = 0,
	        //< Initialization was successful.
	        FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER,
	        //< The library was not compiled with support for the given container format.
	        FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS,
	        //< A required callback was not supplied.
	        FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR,
	        //< An error occurred allocating memory.
	        FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE,
	        //< fopen() failed in FLAC__stream_decoder_init_file() or FLAC__stream_decoder_init_ogg_file().
	        FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED
	        //< FLAC__stream_decoder_init_*() was called when the decoder was  already initialized, usually because FLAC__stream_decoder_finish() was not called.
        }

        public enum FLAC__MetadataType
        {
            FLAC__METADATA_TYPE_STREAMINFO = 0,
            //< <A HREF="../format.html#metadata_block_streaminfo">STREAMINFO</A> block
            FLAC__METADATA_TYPE_PADDING = 1,
            //< <A HREF="../format.html#metadata_block_padding">PADDING</A> block
            FLAC__METADATA_TYPE_APPLICATION = 2,
            //< <A HREF="../format.html#metadata_block_application">APPLICATION</A> block
            FLAC__METADATA_TYPE_SEEKTABLE = 3,
            //< <A HREF="../format.html#metadata_block_seektable">SEEKTABLE</A> block
            FLAC__METADATA_TYPE_VORBIS_COMMENT = 4,
            //< <A HREF="../format.html#metadata_block_vorbis_comment">VORBISCOMMENT</A> block (a.k.a. FLAC tags)
            FLAC__METADATA_TYPE_CUESHEET = 5,
            //< <A HREF="../format.html#metadata_block_cuesheet">CUESHEET</A> block
            FLAC__METADATA_TYPE_PICTURE = 6,
            //< <A HREF="../format.html#metadata_block_picture">PICTURE</A> block
            FLAC__METADATA_TYPE_UNDEFINED = 7
            //< marker to denote beginning of undefined type range; this number will increase as new metadata types are added
        }

        public enum FLAC__ChannelAssignment
        {
            FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, //< independent channels
            FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, //< left+side stereo
            FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, //< right+side stereo
            FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 //< mid+side stereo
        }
        public enum FLAC__FrameNumberType
        {
            FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, //< number contains the frame number
            FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER //< number contains the sample number of first sample in frame
        }
        public enum FLAC__SubframeType
        {
            FLAC__SUBFRAME_TYPE_CONSTANT = 0, //< constant signal
            FLAC__SUBFRAME_TYPE_VERBATIM = 1, //< uncompressed signal
            FLAC__SUBFRAME_TYPE_FIXED = 2, //< fixed polynomial prediction
            FLAC__SUBFRAME_TYPE_LPC = 3 //< linear prediction
        }
        public enum FLAC__EntropyCodingMethodType
        {
	        FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0,
	        //< Residual is coded by partitioning into contexts, each with it's own 4-bit Rice parameter.
	        FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1
	        //< Residual is coded by partitioning into contexts, each with it's own 5-bit Rice parameter.
        }

        public enum FLAC__StreamDecoderErrorStatus
        {
	        FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC,
	        //< An error in the stream caused the decoder to lose synchronization. */
	        FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER,
	        //< The decoder encountered a corrupted frame header. */
	        FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH,
	        //< The frame's data did not match the CRC in the footer. */
	        FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM
	        //< The decoder encountered reserved fields in use in the stream. */
        }
        //=======================================================================================
        //
        [StructLayout(LayoutKind.Explicit)]
        public struct FLAC__FrameHeader
        {
            [FieldOffset(0)]
            public uint blocksize;//unsigned
        	//< The number of samples per subframe.
            [FieldOffset(4)]
            public uint sample_rate;//unsigned
        	//< The sample rate in Hz.
            [FieldOffset(8)]
            public uint channels;//unsigned
        	//< The number of channels (== number of subframes).
            [FieldOffset(12)]
            public FLAC__ChannelAssignment channel_assignment;
        	//< The channel assignment for the frame.
            [FieldOffset(16)]
            public uint bits_per_sample;//unsigned
        	//< The sample resolution.
            [FieldOffset(20)]
            public FLAC__FrameNumberType number_type;
	        //< The numbering scheme used for the frame.  As a convenience, the
	        // decoder will always convert a frame number to a sample number because
	        // the rules are complex.
	        //union {
            [FieldOffset(24)]
            public UInt32 frame_number;// typedef unsigned __int32 FLAC__uint32;
            [FieldOffset(24)]
            public UInt64 sample_number;//typedef unsigned __int64 FLAC__uint64;
        	//};
	        //< The frame number or sample number of first sample in frame;
	        // use the \a number_type value to determine which to use.
            [FieldOffset(28)]
            public byte crc;//typedef unsigned char FLAC__uint8;
	        //< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0)
	        // of the raw frame header bytes, meaning everything before the CRC byte
	        // including the sync code.
        }
        public struct FLAC__Subframe_Constant
        {
            public Int32 value; //< The constant signal value. (FLAC__int32)
        }
        public struct FLAC__Subframe_Verbatim
        {
            public IntPtr data; //< A pointer to verbatim signal. (const FLAC__int32*)
        }

        public struct FLAC__EntropyCodingMethod_PartitionedRiceContents
        {
            //unsigned *parameters;
            public IntPtr parameters;
            //< The Rice parameters for each context.
            //unsigned *raw_bits;
            public IntPtr raw_bitst;
            //< Widths for escape-coded partitions.  Will be non-zero for escaped partitions and zero for unescaped partitions.
            //unsigned capacity_by_order;
            public uint capacity_by_order;
            //< The capacity of the \a parameters and \a raw_bits arrays specified as an order, i.e. the number of array elements allocated is 2 ^ \a capacity_by_order.
        }

        public struct FLAC__EntropyCodingMethod_PartitionedRice
        {
            uint order;// unsigned
            /**< The partition order, i.e. # of contexts = 2 ^ \a order. */
            //const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents;
            IntPtr contents;
            /**< The context's Rice parameters and/or raw bits. */
        }

        public struct FLAC__EntropyCodingMethod
        {
            public FLAC__EntropyCodingMethodType type;
            //union {
            public struct data
            {
                public FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice;
            }
        }

        public struct FLAC__Subframe_Fixed
        {
            public FLAC__EntropyCodingMethod entropy_coding_method;
	        //< The residual coding method.
            public uint order;// unsigned
	        //< The polynomial order.
	        //FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER];//FLAC__MAX_FIXED_ORDER (4u)
            public Int32 warmup0;
            public Int32 warmup1;
            public Int32 warmup2;
            public Int32 warmup3;
	        //< Warmup samples to prime the predictor, length == order.
	        //const FLAC__int32 *residual;
            public IntPtr residual;
	        //< The residual signal, length == (blocksize minus order) samples.
        }

        public struct FLAC__Subframe_LPC
        {
            public FLAC__EntropyCodingMethod entropy_coding_method;
            //< The residual coding method.
            public uint order;// unsigned
            //< The FIR order.
            public uint qlp_coeff_precision;// unsigned
            //< Quantized FIR filter coefficient precision in bits.
            public int quantization_level;
            //< The qlp coeff shift needed.
            //FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER];//FLAC__int32
            public Int32 qlp_coeff00, qlp_coeff01, qlp_coeff02, qlp_coeff03, qlp_coeff04, qlp_coeff05, qlp_coeff06, qlp_coeff07, qlp_coeff08, qlp_coeff09,
            qlp_coeff10,qlp_coeff11,qlp_coeff12,qlp_coeff13,qlp_coeff14,qlp_coeff15,qlp_coeff16,qlp_coeff17,qlp_coeff18,qlp_coeff19,
            qlp_coeff20,qlp_coeff21,qlp_coeff22,qlp_coeff23,qlp_coeff24,qlp_coeff25,qlp_coeff26,qlp_coeff27,qlp_coeff28,qlp_coeff29,
            qlp_coeff30,qlp_coeff31;
            //< FIR filter coefficients.
            //FLAC__int32 warmup[FLAC__MAX_LPC_ORDER];//FLAC__int32
            public Int32 warmup00, warmup01, warmup02, warmup03, warmup04, warmup05, warmup06, warmup07, warmup08, warmup09,
            warmup10,warmup11,warmup12,warmup13,warmup14,warmup15,warmup16,warmup17,warmup18,warmup19,
            warmup20,warmup21,warmup22,warmup23,warmup24,warmup25,warmup26,warmup27,warmup28,warmup29,
            warmup30,warmup31;
            //< Warmup samples to prime the predictor, length == order.
            //const FLAC__int32 *residual;
            public IntPtr residual;
            //< The residual signal, length == (blocksize minus order) samples.
        }


        public struct FLAC__Subframe
        {
            public FLAC__SubframeType type;
            //union {
            [StructLayout(LayoutKind.Explicit)]
            public struct _data
            {
                [FieldOffset(0)]
                public FLAC__Subframe_Constant constant;
                [FieldOffset(0)]
                public FLAC__Subframe_Fixed fixed_;
                [FieldOffset(0)]
                public FLAC__Subframe_LPC lpc;
                [FieldOffset(0)]
                public FLAC__Subframe_Verbatim verbatim;
            }
            public _data data;
            public uint wasted_bits;// unsigned
        }

        public struct FLAC__FrameFooter
        {
            public UInt16 crc;//FLAC__uint16 crc;
        //< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with 0) of the bytes before the crc, back to and including the frame header sync code.
        }

        public struct FLAC__Frame
        {
            public FLAC__FrameHeader header;
            //FLAC__Subframe subframes[FLAC__MAX_CHANNELS];//FLAC__MAX_CHANNELS (8u)
            public FLAC__Subframe subframes0;
            public FLAC__Subframe subframes1;
            public FLAC__Subframe subframes2;
            public FLAC__Subframe subframes3;
            public FLAC__Subframe subframes4;
            public FLAC__Subframe subframes5;
            public FLAC__Subframe subframes6;
            public FLAC__Subframe subframes7;
            public FLAC__FrameFooter footer;
        }
        public struct FLAC__StreamDecoder
        {
            //struct FLAC__StreamDecoderProtected *protected_; // avoid the C++ keyword 'protected' 
            IntPtr protected_;
            //struct FLAC__StreamDecoderPrivate *private_; // avoid the C++ keyword 'private' 
            IntPtr private_;
        } 
        //
        public struct FLAC__StreamMetadata_StreamInfo
        {
            public uint min_blocksize, max_blocksize;// unsigned
            public uint min_framesize, max_framesize;// unsigned
            public uint sample_rate;// unsigned
            public uint channels;// unsigned
            public uint bits_per_sample;// unsigned
            public UInt64 total_samples;// FLAC__uint64
            public byte //FLAC__byte [16]
            md5sum01, md5sum02, md5sum03, md5sum04, md5sum05, md5sum06, md5sum07, md5sum08, md5sum09, md5sum10, md5sum11, md5sum12, md5sum13, md5sum14, md5sum15;
        }

        public struct FLAC__StreamMetadata{
	        public FLAC__MetadataType type;
	        //< The type of the metadata block; used determine which member of the \a data union to dereference.  If type >= FLAC__METADATA_TYPE_UNDEFINED then \a data.unknown must be used. */
            public bool is_last;//FLAC__bool
	        /**< \c true if this metadata block is the last, else \a false */
            public uint length;//unsigned
	        /**< Length, in bytes, of the block data as it appears in the stream. */
            [StructLayout(LayoutKind.Explicit)]
            public struct _data
            {
                [FieldOffset(0)]
                public FLAC__StreamMetadata_StreamInfo stream_info;
                /*
                [FieldOffset(0)]
                public FLAC__StreamMetadata_Padding padding;
                [FieldOffset(0)]
                public FLAC__StreamMetadata_Application application;
                [FieldOffset(0)]
                public FLAC__StreamMetadata_SeekTable seek_table;
                [FieldOffset(0)]
                public FLAC__StreamMetadata_VorbisComment vorbis_comment;
                [FieldOffset(0)]
                public FLAC__StreamMetadata_CueSheet cue_sheet;
                [FieldOffset(0)]
                public FLAC__StreamMetadata_Picture picture;
                [FieldOffset(0)]
                public FLAC__StreamMetadata_Unknown unknown;
                 */
	        };
            public _data data;
	        //< Polymorphic block data; use the \a type value to determine which to use. */
        }
        //

        [DllImport("libFLAC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr FLAC__stream_decoder_new();
        //public static extern FLAC__StreamDecoder FLAC__stream_decoder_new();
        //
        [DllImport("libFLAC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool FLAC__stream_decoder_set_md5_checking(IntPtr decoder, bool value);
        //FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value);
        //
        [DllImport("libFLAC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file(IntPtr decoder, string file, MulticastDelegate write_callback, MulticastDelegate metadata_callback, MulticastDelegate error_callback, BinaryWriter client_data); 
        //FLAC__StreamDecoder *decoder, FILE *file, FLAC__StreamDecoderWriteCallback write_callback, FLAC__StreamDecoderMetadataCallback metadata_callback, FLAC__StreamDecoderErrorCallback error_callback, void *client_data); 

        [DllImport("libFLAC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern string FLAC__StreamDecoderInitStatusString(int init_status);

        [DllImport("libFLAC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool FLAC__stream_decoder_process_until_end_of_stream(IntPtr decoder);

        [DllImport("libFLAC.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void FLAC__stream_decoder_delete(IntPtr decoder);

        public static UInt64 total_samples = 0;
        public static uint sample_rate = 0;
        public static uint channels = 0;
        public static uint bps = 0;

        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        public delegate FLAC__StreamDecoderWriteStatus d_write_callback(IntPtr decoder, IntPtr frame, IntPtr buffer, BinaryWriter client_data);
        //
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        public delegate void d_metadata_callback(IntPtr decoder, IntPtr metadata, IntPtr client_data);
        //
        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
        public delegate void d_error_callback(IntPtr decoder, FLAC__StreamDecoderErrorStatus status, IntPtr client_data);
        //
        //
        static int Main(string[] args)
        {
            string InputFile = @"c:\__DocBackup\04 Six Feet Under.flac";
            string OutputFile = @"c:\__DocBackup\out.wav";
            bool checkMd5 = true;
            //
            //FLAC__StreamDecoder *decoder = 0;
            IntPtr decoder = IntPtr.Zero;
            FLAC__StreamDecoderInitStatus init_status;
            BinaryWriter bw = null;
            //
            if (0 == args.Count())
            {
                Usage();
                return 3;
            }
            //
            if (1 == args.Count())
            {
                if (0 == string.Compare("-environment", args[0], true))
                {
                    InputFile = Environment.GetEnvironmentVariable("___INPUT FILE").Trim();
                    OutputFile = Environment.GetEnvironmentVariable("___OUTPUT FILE").Trim();
                    checkMd5 = bool.Parse(Environment.GetEnvironmentVariable("___CHECK MD5").Trim());
                }
                else
                {
                    Usage();
                    return 3;
                }
            }
            else //args.Count() != 1
            {
                int n = 0;
                if (0 == string.Compare("-md5-check", args[n], true))
                {
                    checkMd5 = true;
                    n++;
                }
                //
                if (2 != (args.Count() - n))
                {
                    Usage();
                    return 3;
                }
                //
                InputFile = args[n].Trim();
                OutputFile = args[n+1].Trim();
                //
            }
            //
            try
            {
                bw = new BinaryWriter(new FileStream(OutputFile, FileMode.Create, FileAccess.Write, FileShare.Read));
            }
            catch(Exception e)
            {
                Console.WriteLine("ERROR: opening [{0}] for output {1}", OutputFile, e.Message);
                return 99;
            }
            //
            //
            try
            {
                //
                if (IntPtr.Zero == (decoder = (IntPtr)FLAC__stream_decoder_new()))
                {
                    Console.WriteLine("ERROR: allocating decoder");
                    bw.Close();
                    return 10;
                }
                //
                FLAC__stream_decoder_set_md5_checking(decoder, checkMd5);
                //
                d_write_callback d_w_c = write_callback;
                d_metadata_callback d_m_c = metadata_callback;
                d_error_callback d_e_c = error_callback;
                //
                init_status = FLAC__stream_decoder_init_file(decoder, InputFile, d_w_c, d_m_c, d_e_c, /*client_data=*/bw);
                if (init_status != FLAC__StreamDecoderInitStatus.FLAC__STREAM_DECODER_INIT_STATUS_OK)
                {
                    //fprintf(stderr, "ERROR: initializing decoder: %s\n", FLAC__StreamDecoderInitStatusString[init_status]);
                    throw new Exception(string.Format("ERROR: initializing decoder: {0}", init_status));
                }
                else
                {
                    if (FLAC__stream_decoder_process_until_end_of_stream(decoder))
                    {
                        Console.WriteLine("Decoding succeeded");
                    }
                    else
                    {
                        throw new Exception("ERROR: decoding FAILED");
                    }
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: {0}", e.Message);
                //
                if (null != bw) bw.Close();
                if (File.Exists(OutputFile)) File.Delete(OutputFile);
                //
                return 100;
            }
            finally
            {
                if(IntPtr.Zero != decoder)FLAC__stream_decoder_delete(decoder);
                if (null != bw) bw.Close();
            }
            //
            return 0;// OK
        }//main
        //
        public static void Usage()
        {
            Console.WriteLine("Usage: {0} -environment", System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]));
            Console.WriteLine("Environment variables (with spaces!):");
            Console.WriteLine("\t[___INPUT FILE] - input file name (.flac)");
            Console.WriteLine("\t[___OUTPUT FILE] - output file name (.wav)");
            Console.WriteLine("\t[___CHECK MD5] - true|false");
            Console.WriteLine("or");
            Console.WriteLine("Usage: {0} [-md5-check] InputFile.flac OutputFile.wav", System.IO.Path.GetFileName(Environment.GetCommandLineArgs()[0]));
            return;
        }
        //
        //FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
        public static FLAC__StreamDecoderWriteStatus write_callback(IntPtr decoder, IntPtr frame, [MarshalAs(UnmanagedType.LPArray)] IntPtr buffer, BinaryWriter bw)
        {
            //FILE *f = (FILE*)client_data;
            //IntPtr f = client_data;
            //const FLAC__uint32 total_size = (FLAC__uint32)(total_samples * channels * (bps/8));
            UInt32 total_size = (UInt32)(total_samples * channels * (bps / 8));
            //size_t i;
            //uint i;

            FLAC__Frame frm = new FLAC__Frame();
            frm = (FLAC__Frame)Marshal.PtrToStructure(frame, typeof(FLAC__Frame));

        if(total_samples == 0)
        {
            Console.WriteLine("ERROR: this example only works for FLAC files that have a total_samples count in STREAMINFO");
            return FLAC__StreamDecoderWriteStatus.FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
        }
        if(channels != 2 || bps != 16)
        {
            Console.WriteLine("ERROR: this example only supports 16bit stereo streams");
            return FLAC__StreamDecoderWriteStatus.FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
        }

        /* write WAVE header before we write the first frame */
        if(frm.header.sample_number == 0)
        {
            //
            try
            {
                //bw.Write("RIFF");
                bw.Write(new Byte[] { 0x52, 0x49, 0x46, 0x46 });
                bw.Write((UInt32)(total_size + 36));
                //bw.Write("WAVEfmt ");
                bw.Write(new Byte[] { 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20 });
                bw.Write((UInt32)(16));
                bw.Write((UInt16)(1));
                bw.Write((UInt16)(channels));
                bw.Write((UInt32)(sample_rate));
                bw.Write((UInt32)(sample_rate * channels * (bps / 8)));
                bw.Write((UInt16)(channels * (bps / 8)));
                bw.Write((UInt16)(bps));
                //bw.Write("data");
                bw.Write(new Byte[] { 0x64, 0x61, 0x74, 0x61 });
                bw.Write((UInt32)(total_size));
            }
            catch (Exception e)
            {
                Console.WriteLine("ERROR: write error {0}", e.Message);
                return FLAC__StreamDecoderWriteStatus.FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
            }
            //
        }
            //
        IntPtr pLeft = IntPtr.Zero;
        IntPtr pRight = IntPtr.Zero;
        UInt16 uLeft, uRight;
        /* write decoded PCM samples */
        try
        {
            pLeft = Marshal.ReadIntPtr(buffer, 0);
            pRight = Marshal.ReadIntPtr(buffer, 4);
            for (int i = 0; i < frm.header.blocksize; i++)
            {
                uLeft = (UInt16)Marshal.ReadInt16(pLeft, i * 4);
                uRight = (UInt16)Marshal.ReadInt16(pRight, i * 4);
                bw.Write(uLeft); bw.Write(uRight);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("ERROR: write error {0}", e.Message);
            return FLAC__StreamDecoderWriteStatus.FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
        }

        return FLAC__StreamDecoderWriteStatus.FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
        }

        //void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
        public static void metadata_callback(IntPtr decoder, IntPtr metadata, IntPtr client_data)
        {
            FLAC__StreamMetadata sm = new FLAC__StreamMetadata();
            sm = (FLAC__StreamMetadata)Marshal.PtrToStructure(metadata, typeof(FLAC__StreamMetadata));
            /* print some stats */
            if(sm.type == FLAC__MetadataType.FLAC__METADATA_TYPE_STREAMINFO) {
                // save for later
                total_samples = sm.data.stream_info.total_samples;
                sample_rate = sm.data.stream_info.sample_rate;
                channels = sm.data.stream_info.channels;
                bps = sm.data.stream_info.bits_per_sample;

                Console.WriteLine("Sample rate    : {0} Hz", sample_rate);
                Console.WriteLine("Channels       : {0}", channels);
                Console.WriteLine("Bits per sample: {0}", bps);
                Console.WriteLine("Total samples  : {0}", total_samples);
            }
        }
        //
        //void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
        public static void error_callback(IntPtr decoder, FLAC__StreamDecoderErrorStatus status, IntPtr client_data)
        {
            //printf("Got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
            Console.WriteLine("ERROR: got error callback: {0}", status);
        }
        //
    }
}
